/**
 * 
 */
package vue;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import vue.desktop.IGDesktop;
import vue.globalBar.IGGlobalExecutionBar;
import vue.session.IGCNFUse;
import vue.session.IGExecutionBlock;
import vue.session.IGPreprocessorBlock;
import vue.session.IGSolverBlock;
import vue.session.IIGModificationListener;

import model.EErrorPopUp;
import model.cnf.CNF;
import model.session.CNFUse;
import model.session.ExecutableSessionBlock;
import model.session.PreprocessorExecutionBlock;
import model.session.SolverExecutionBlock;
import controller.ISessionManager;

/**
 * @author GRONOWSKI Stephane svan001@aol.com
 * 
 *         igsat 2010
 * 
 */
public class IGSessionManager extends JPanel implements IIGSessionManager {

	private static final long										serialVersionUID			= 1L;
	public static final String										DEFAULT_ERROR_POPUP_TITLE	= "ERROR !!";


	private ISessionManager											manager;
	private BorderLayout											layout;
	private IGDesktop												desktop;
	private IGGlobalExecutionBar									globalBar;
	private Set<IIGModificationListener>							listeners					= new HashSet<IIGModificationListener>();
	private Set<IIGSATPart>											parts						= new HashSet<IIGSATPart>();

	// private IGCNFUse first;

	private Map<ExecutableSessionBlock, IGExecutionBlock>			executions					= new HashMap<ExecutableSessionBlock, IGExecutionBlock>();
	private Map<CNFUse, IGCNFUse>									cnfUses						= new HashMap<CNFUse, IGCNFUse>();
	private Map<SolverExecutionBlock, IGSolverBlock>				solvers						= new HashMap<SolverExecutionBlock, IGSolverBlock>();
	private Map<PreprocessorExecutionBlock, IGPreprocessorBlock>	preprocessors				= new HashMap<PreprocessorExecutionBlock, IGPreprocessorBlock>();

	// private IGCNFExecutionBar cnfbar;


	public IGSessionManager(CNFUse cnf, ISessionManager manager) {
		// TODO Auto-generated constructor stub
		this.manager = manager;
		layout = new BorderLayout();
		setLayout(layout);

		// ajoute desktop
		desktop = new IGDesktop(manager);
		add(desktop, BorderLayout.CENTER);
		parts.add(desktop);
		listeners.add(desktop);

		// ajoute barre global
		globalBar = new IGGlobalExecutionBar(manager);
		add(globalBar, BorderLayout.EAST);
		parts.add(globalBar);
		listeners.add(globalBar);

		// this.first =
		addCNFUse(cnf);
		updateCNFUse(cnf);

		// cnfbar = newcnf.getExecutionBar();
		// add(cnfbar, BorderLayout.SOUTH);


		// validate();
	}

	@Override
	public void addRunningExecution(ExecutableSessionBlock exec, CNFUse cnf) {
		// TODO A tester

		IGExecutionBlock block = new IGExecutionBlock(cnfUses.get(cnf), exec, listeners);

		executions.put(exec, block);
		for (IIGSATPart part : parts)
			part.addExecution(cnfUses.get(cnf), block);

	}

	@Override
	public void removeRunningExecution(ExecutableSessionBlock exec, CNFUse cnf) {
		// TODO A tester
		IGExecutionBlock block = executions.remove(exec);

		if (block == null)
			return;

		block.notifyRemoved();
	}


	/*
	 * @Override public void showAll(CNFUse cnf) { // TODO Auto-generated method stub
	 * 
	 * }
	 */

	@Override
	public void showErrorPopup(EErrorPopUp type, String message) {
		// TODO A tester
		JOptionPane.showMessageDialog(null, type.getPrefix() + "\n" + message + "\n" + type.getSuffix(), DEFAULT_ERROR_POPUP_TITLE,
				JOptionPane.ERROR_MESSAGE);
	}

	@Override
	public void updateCNFInformation(CNFUse cnf) {
		// TODO A tester
		IGCNFUse tmp = cnfUses.get(cnf);
		if (tmp != null)
			tmp.notifyModified();
	}

	@Override
	public void updateCNFUse(CNFUse cnf) {
		// TODO A tester
		System.out.println("check cnfuse");
		IGCNFUse igcnfuse = cnfUses.get(cnf);
		if (igcnfuse == null)
			return;

		updateCNFInformation(cnf);

		// check solver
		Set<IGSolverBlock> alreadysolver = new HashSet<IGSolverBlock>();
		alreadysolver.addAll(igcnfuse.getSolvers());
		for (SolverExecutionBlock sol : cnf.getSolver())
		{
			IGSolverBlock block = solvers.get(sol);
			if (block != null)
			{// cas existe deja
				alreadysolver.remove(block);
				block.notifyModified();
				continue;
			}
			// existe pas
			addSolver(sol, igcnfuse);
		}
		if (!alreadysolver.isEmpty())
		{// des suppressions
			for (IGSolverBlock b : alreadysolver)
				removeSolver(b, igcnfuse);
		}

		// check preproc
		Set<IGPreprocessorBlock> alreadypreproc = new HashSet<IGPreprocessorBlock>();
		alreadypreproc.addAll(igcnfuse.getPreprocs().keySet());

		for (PreprocessorExecutionBlock pre : cnf.getPreproc().keySet())
		{
			IGPreprocessorBlock block = preprocessors.get(pre);
			if (block != null)
			{// cas existe deja
				alreadypreproc.remove(block);
				block.notifyModified();
				updateCNFUse(cnf.getPreproc().get(pre));
				continue;
			}
			// existe pas
			IGCNFUse res = null;
			if (cnf.getPreproc().get(pre) != null)
				res = addCNFUse(cnf.getPreproc().get(pre));

			System.out.println("res= " + res);
			addPreprocessor(igcnfuse, pre, res);
		}
		if (!alreadypreproc.isEmpty())
		{// des suppressions
			for (IGPreprocessorBlock b : alreadypreproc)
				removePreprocessor(igcnfuse, b);
		}

		igcnfuse.notifyModified();

	}

	/**
	 * Add a {@link CNFUse} to the current session.
	 * 
	 * @param cnfuse
	 *            the {@link CNFUse} to add
	 * @return the res
	 */
	private IGCNFUse addCNFUse(CNFUse cnfuse) {
		IGCNFUse tmp = new IGCNFUse(cnfuse, listeners);

		cnfUses.put(cnfuse, tmp);
		for (IIGSATPart part : parts)
			part.addCNFUse(tmp);

		return tmp;
	}

	/**
	 * 
	 * Add a {@link IGSolverBlock} to the current session.
	 * 
	 * @param solver
	 *            the {@link SolverExecutionBlock} to add
	 * @param cnfuse
	 *            the mother {@link IGCNFUse}
	 */
	private void addSolver(SolverExecutionBlock solver, IGCNFUse cnfuse) {
		IGSolverBlock block = new IGSolverBlock(solver, listeners);

		System.out.println("add solver");

		solvers.put(solver, block);
		cnfuse.addSolver(block);
		for (IIGSATPart part : parts)
			part.addSolver(cnfuse, block);
	}

	/**
	 * Add a {@link IGPreprocessorBlock} to the session
	 * 
	 * @param mother
	 *            the mother {@link IGCNFUse}
	 * @param preproc
	 *            the {@link PreprocessorExecutionBlock} to add
	 * @param daughter
	 *            the daughter {@link IGCNFUse}
	 */
	private void addPreprocessor(IGCNFUse mother, PreprocessorExecutionBlock preproc, IGCNFUse daughter) {
		IGPreprocessorBlock block = new IGPreprocessorBlock(preproc, listeners);

		preprocessors.put(preproc, block);
		mother.addPreproc(block, daughter);
		for (IIGSATPart part : parts)
			part.addPreprocessor(mother, block, daughter);
	}

	/**
	 * Remove a {@link IGCNFUse} from the current session.
	 * 
	 * @param cnfuse
	 *            the {@link IGCNFUse} to remove
	 */
	private void removeCNFUse(IGCNFUse cnfuse) {
		cnfUses.remove(cnfuse.getCnfuse());

		// solver
		Set<IGSolverBlock> tmpSolvers = new HashSet<IGSolverBlock>();
		tmpSolvers.addAll(cnfuse.getSolvers());

		for (IGSolverBlock block : tmpSolvers)
			removeSolver(block, cnfuse);

		// preproc
		Map<IGPreprocessorBlock, IGCNFUse> tmp = new HashMap<IGPreprocessorBlock, IGCNFUse>();
		tmp.putAll(cnfuse.getPreprocs());

		for (IGPreprocessorBlock block : tmp.keySet())
		{
			IGCNFUse cnf = tmp.get(block);
			removePreprocessor(cnfuse, block);
			// removeCNFUse(cnf);
		}

		cnfuse.notifyRemoved();
	}

	/**
	 * 
	 * Remove a {@link IGSolverBlock} to the current session.
	 * 
	 * @param solver
	 *            the {@link IGSolverBlock} to remove
	 * @param cnfuse
	 *            the mother {@link IGCNFUse}
	 */
	private void removeSolver(IGSolverBlock solver, IGCNFUse cnfuse) {

		solvers.remove(solver.getSolver());
		cnfuse.removeSolver(solver);
		solver.notifyRemoved();
	}

	/**
	 * Remove a {@link IGPreprocessorBlock} to the session
	 * 
	 * @param mother
	 *            the mother {@link IGCNFUse}
	 * @param preproc
	 *            the {@link IGPreprocessorBlock} to remove
	 */
	private void removePreprocessor(IGCNFUse mother, IGPreprocessorBlock preproc) {

		preprocessors.remove(preproc.getPreproc());

		IGCNFUse res = mother.removePreproc(preproc);
		preproc.notifyRemoved();
		if(res != null)
			removeCNFUse(res);
	}

	/**
	 * Close the session.
	 * 
	 * @return if it close correctly.
	 */
	public boolean close() {
		return true;
	}

	@Override
	public ISessionManager getSessionManager() {
		return manager;
	}

}
